package org.example.java_gobang.api;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.java_gobang.game.*;
import org.example.java_gobang.model.User;
import org.example.java_gobang.model.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Component
public class GameAPI extends TextWebSocketHandler {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private RoomManager roomManager;
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        GameReadyResponse resp = new GameReadyResponse();
        //1.先获取到用户的身份信息(从HttpSession获取)
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            resp.setOk(false);
            resp.setReason("⽤⼾尚未登录!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        //2.判断当前玩家是否已经进入房间(使用房间管理器查询)
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            resp.setOk(false);
            resp.setReason("⽤⼾并未匹配成功! 不能开始游戏!");
            session.sendMessage(new
                    TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        System.out.println("连接游戏! roomId=" + room.getRoomId() + ", userId=" +
                user.getUserId());
        // 3.先判定⽤⼾是不是已经在游戏中了.
        if (onlineUserManager.getSessionFromGameHall(user.getUserId()) != null
                || onlineUserManager.getSessionFromGameRoom(user.getUserId()) != null) {
            resp.setOk(false);
            resp.setReason("禁⽌多开游戏⻚⾯!");
            resp.setMessage("repeatConnection");

            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        // 4.更新会话,设置玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);


        //5. 同⼀个房间的两个玩家, 同时连接时要考虑线程安全问题.
        //  把两个玩家加入到游戏房间中
        // 前面创建房间 / 匹配过程，是在 game_hall.html 页面中完成的。
        // 当匹配到对手之后，就要经过页面跳转，来到 game_room.html 中才算真正进入房间。
        // 页面跳转，是有可能失败的！！// 更准确的做法，两个玩家都进入 game_room.html，才算是真正准备好进入房间了。
        // 当执行到这个逻辑的时候，说明玩家已经进入到 game_room.html 页面了。

        synchronized (room) {
            // 第一个玩家还尚未进入房间
            // 就把当前连上 websocket 的玩家作为 user1，加入房间中。
            if (room.getUser1() == null) {
                room.setUser1(user);
                // 设置 userId1 为先⼿⽅
                room.setWhiteUserId(user.getUserId());
                System.out.println("玩家=" + user.getUsername() + " 玩家1准备就绪!");
                return;
            }
            if (room.getUser2() == null) {
                room.setUser2(user);
                System.out.println("玩家=" + user.getUsername() + " 玩家2准备就绪!");
                // 通知玩家1 就绪
                noticeGameReady(room, room.getUser1().getUserId(), room.getUser2().getUserId());
                // 通知玩家2 就绪
                noticeGameReady(room, room.getUser2().getUserId(), room.getUser1().getUserId());
                return;
            }
        }
        //6又有玩家试图连接该房间(理论上不存在)    房间已经满了!
        resp.setOk(false);
        String log = "roomId=" + room.getRoomId() + " 已经满了! 连接游戏失败!";
        resp.setReason(log);
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
        System.out.println(log);
    }

    private void noticeGameReady(Room room, int thisUserId, int thatUserId) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setRoomId(room.getRoomId());

        resp.setThisUserId(thisUserId);
        resp.setThatUserId(thatUserId);
        resp.setWhiteUserId(room.getWhiteUserId());
        WebSocketSession session1 = onlineUserManager.getSessionFromGameRoom(thisUserId);
        session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //1.先从会话拿到当前的用户信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            System.out.println("当前玩家尚未登录");
            return;
        }
        //2.根据玩家id,获取游戏房间
        Room room = roomManager.getRoomByUserId(user.getUserId());

        // 3. 解析消息类型
        String payload = message.getPayload();
        GameRequest req = objectMapper.readValue(payload, GameRequest.class);

        // 4. 如果是聊天消息，转发给房间内另一个玩家
        if ("chat".equals(req.getMessage())) {
            // 构造聊天响应
            GameResponse chatResp = new GameResponse();
            chatResp.setMessage("chat"); // 标记为聊天消息
            chatResp.setUserId(user.getUserId()); // 发送者ID
            chatResp.setContent(req.getContent()); // 消息内容

            // 获取房间内另一个玩家的会话
            WebSocketSession otherSession = null;
            if (room.getUser1().getUserId() == user.getUserId()) {
                otherSession = onlineUserManager.getSessionFromGameRoom(room.getUser2().getUserId());
            } else {
                otherSession = onlineUserManager.getSessionFromGameRoom(room.getUser1().getUserId());
            }

            // 转发消息给对方
            if (otherSession != null) {
                otherSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(chatResp)));
            }
            // 同时发送给自己（新增这行代码）
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(chatResp)));
            return; // 处理完聊天消息后返回
        }

        // 5. 原有落子逻辑（保持不变）
        if ("putChess".equals(req.getMessage())) {
            room.putChess(payload);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

        //1.从会话里面拿到用户信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            //简单处理
            return;
        }
        //2.判定用户是否在线,如果在线就从哈希表中删除
        WebSocketSession exitSession = onlineUserManager.getSessionFromGameRoom(user.getUserId());
        if (session == exitSession) {
            //通过判定,避免多开情况下第二个用户的退出连接把前面的给提了
            onlineUserManager.exitGameRoom(user.getUserId());

        }
        System.out.println("当前用户: " + user.getUsername() + "游戏房间连接异常");
        //更新玩家的分数信息

        //掉线就通知对手获胜了
        noticeThatUserWin(user);

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

        //1.从会话里面拿到用户信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            //简单处理
            return;
        }
        //2.判定哟胡是否在线,如果在线就从哈希表中删除
        WebSocketSession exitSession = onlineUserManager.getSessionFromGameRoom(user.getUserId());
        if (session == exitSession) {
            //通过判定,避免多开情况下第二个相同用户的退出连接把前面的给提了
            onlineUserManager.exitGameRoom(user.getUserId());

        }
        System.out.println("当前用户: " + user.getUsername() + "离开游戏房间");



        //掉线就通知对手获胜了
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        //1.根据当前玩家,找到玩家在的房间
        Room room =  roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            //这个情况表示房间已经被释放
            System.out.println("当前房间被释放");
            return;
        }

        //2.根据房间找到对手,当前你是这个房间的一号玩家,那么对手就是这个房间的二号玩家,反之对手是一号玩家
        User thatUser = (user == room.getUser1() ? room.getUser2() : room.getUser1());
        //3.根据对手的ID找到对手的在线状态
        WebSocketSession webSocketSession = onlineUserManager.getSessionFromGameRoom(thatUser.getUserId());
        if(webSocketSession == null){
            //这就意味这对手也掉线了!
            System.out.println("对手也掉线了,无需通知对手");
            return;
        }
        //4.对手是正常的话,我掉线就会构造响应,通知对手获胜

        GameResponse resp = new GameResponse();
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        resp.setMessage("putChess");
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        //5.更新玩家的分数信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        //6.释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
    }
}
